﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;

namespace Red.GameData.Raw
{
    /// <summary>
    /// A static information about a property in a reflection class
    /// </summary>
    public class ReflectionProperty
    {
        /// <summary>
        /// Name of the property
        /// </summary>
        public string Name { get; private set; }

        /// <summary>
        /// Parent reflection class this property was defined in
        /// Can be used as a cheap way to get to the ReflectionData "singleton"
        /// </summary>
        public ReflectionClass Parent { get; private set; }

        /// <summary>
        /// Is this an optional property ?
        /// Optional properties don't HAVE to define a value in the objects
        /// If there's no value for such property in an object a pattern of zeros is used instead
        /// </summary>
        public bool IsOptional { get; private set; }

        /// <summary>
        /// Is this inlined property ?
        /// Inlined property (useful only in case of object references) allow to define the referenced object directly in-place of the parent object
        /// This is a major improvement in the data base as not all objects will have to be named to be accessed
        /// </summary>
        public bool IsInlined { get; private set; }

        /// <summary>
        /// Is this property visible in the editor's property grids or other general tools ?
        /// Setting the IsVisible flag to false is good to hide very low-level properties (e.g. position of the graph block) from generic property grids
        /// Such properties can still be modified directly by other code
        /// </summary>
        public bool IsVisible { get; private set; }

        /// <summary>
        /// Is this property considered "advanced" ?
        /// Advanced properties are not displayed unless the "Show advanced properties" button is toggled on
        /// </summary>
        public bool IsAdvanced { get; private set; }

        /// <summary>
        /// Is this a read only property ?
        /// Read only properties will NOT be modified via ANY of the interfaces inside the tools
        /// This is only useful if we have some hand-defined data somewhere and we really, really don't want the users to change those values
        /// </summary>
        public bool IsReadOnly { get; private set; }

        /// <summary>
        /// Is this an editor only property?
        /// </summary>
        public bool IsEditorOnly { get; private set; }

        /// <summary>
        /// Is this an array, not a single value?
        /// Changes how to interpret the resolved values array
        /// For arrays we keep all of the elements that survived the resolve
        /// For non-arrays we keep only the latest one
        /// </summary>
        public bool IsArray { get; private set; }

        /// <summary>
        /// Resolved type describing type of value for this property
        /// This is the only way to get to know how to interpret the strings that are stored in the system
        /// </summary>
        public ReflectionType Type { get; private set; }

        /// <summary>
        /// Creator defined comment (user hint) for this property
        /// Usually displayed in the tooltip
        /// </summary>
        public string Comment { get; private set; }

        /// <summary>
        ///  Retrieve the category that this property falls into
        /// </summary>
        public string Category
        {
            get
            {
                // use the
                if (_editorCategory != "")
                    return _editorCategory;

                // use the name of the class if no custom category was specified
                return Parent.Name;
            }
        }

        /// <summary>
        ///  Retrieve the category that this property falls into
        /// </summary>
        public string CustomEditor
        {
            get
            {
                return _editorCustomEditor;
            }
        }

        /// <summary>
        /// Create the property holder
        /// </summary>
        private ReflectionProperty(ReflectionClass parent, string name, string typeName)
        {
            Parent = parent;
            Name = name;
            _typeName = typeName;
        }

        /// <summary>
        ///  XML shitty parsing - Load property from XML node
        /// </summary>
        /// <param name="parent">Parent reflection class</param>
        /// <param name="log">Progress/error reporting interface</param>
        /// <param name="node">XML node to load from</param>
        /// <returns>True if the loading was successful or false if there were errors</returns>
        public static bool LoadFromNode(ReflectionClass parent, IGenericFileErrorReporter log, XmlNode node)
        {
            // 'name' attribute is required
            string propName = Raw.Utils.Attr(node, "name");
            if (propName == "")
            {
                log.Error(String.Format("Missing attribute 'name' for property of class {0}", parent.Name));
                return false;
            }

            // 'type' attribute is also required
            string propType = Raw.Utils.Attr(node, "type");
            if (propType == "")
            {
                log.Error(String.Format("Missing attribute 'tyoe' for property {1} of class {0}", parent.Name, propName));
                return false;
            }

            // create reflection property 
            var prop = new ReflectionProperty(parent, propName, propType);
            parent.ClassProperties.Add(prop);

            // load property flags
            prop.IsInlined = Raw.Utils.Attr(node, "inline", false) || Raw.Utils.Attr(node, "inplace", false);
            prop.IsArray = Raw.Utils.Attr(node, "array", false);
            prop.IsOptional = Raw.Utils.Attr(node, "optional", false);
            prop.IsVisible = !Raw.Utils.Attr(node, "hidden", false);
            prop.IsAdvanced = Raw.Utils.Attr(node, "advanced", false);
            prop.IsReadOnly = Raw.Utils.Attr(node, "readonly", false);
            prop.IsEditorOnly = Raw.Utils.Attr( node, "editorOnly", false );

            // additional stuff
            prop._editorCategory = Raw.Utils.Attr(node, "category");
            prop._editorCustomEditor = Raw.Utils.Attr(node, "editor");

            // default value
            var defaultValueStr = Raw.Utils.AttrMaybe(node, "default");
            if (defaultValueStr!= null)
            {
                // inplace default value creation (syntax shortcut)
                var defaultValue = new ReflectionDefaultValue(parent, propName, defaultValueStr);
                parent.ClassDefaultValues.Add(defaultValue);
            }

            // no loading errors
            return true;
        }

        /// <summary>
        /// Resolve type name -> actual type
        /// Done after loading all of the files on disk
        /// </summary>
        /// <param name="log">Generall error reporting interface</param>
        /// <returns>True if the type was resolved for this property, false otherwise</returns>
        public bool ResolveType(IGenericFileErrorReporter log)
        {
            // resolve type4
            if (Type == null)
                Type = Parent.ReflectionData.FindType(_typeName);

            // invalid type
            if (Type == null)
            {
                log.Error(String.Format("Unable to resolve type {0} for property {1} in class {2}", _typeName, Parent.Name, (Parent.Parent!=null) ? Parent.Parent.Name : "unknown"));
                return false;
            }

            // type valid or resolved
            return true;
        }

        /// <summary>
        /// A name of the property type - used before the type name is resolved
        /// </summary>
        private readonly string _typeName;

        /// <summary>
        /// Display category this property should be classified into
        /// If not specified then the name of the class is used
        /// </summary>
        private string _editorCategory;

        /// <summary>
        /// Custom editor type
        /// </summary>
        private string _editorCustomEditor;
    }
}
